/* -*- mode: js2; js2-basic-offset: 4; -*- */
-const Shell = imports.gi.Shell;
const Clutter = imports.gi.Clutter;
+const Shell = imports.gi.Shell;
+const Signals = imports.signals;
+const Tweener = imports.tweener.tweener;
const Panel = imports.ui.panel;
const Overlay = imports.ui.overlay;
let run_dialog = null;
let wm = null;
+// The "FrameTicker" object is an object used to feed new frames to Tweener
+// so it can update values and redraw. The default frame ticker for
+// Tweener just uses a simple timeout at a fixed frame rate and has no idea
+// of "catching up" by dropping frames.
+//
+// We substitute it with custom frame ticker here that connects Tweener to
+// a Clutter.TimeLine. Now, Clutter.Timeline itself isn't a whole lot more
+// sophisticated than a simple timeout at a fixed frame rate, but at least
+// it knows how to drop frames. (See HippoAnimationManager for a more
+// sophisticated view of continous time updates; even better is to pay
+// attention to the vertical vblank and sync to that when possible.)
+//
+function ClutterFrameTicker() {
+ this._init();
+}
+
+ClutterFrameTicker.prototype = {
+ TARGET_FRAME_RATE : 60,
+
+ _init : function() {
+ // We don't have a finite duration; tweener will tell us to stop
+ // when we need to stop, so use 1000 seconds as "infinity"
+ this._timeline = new Clutter.Timeline({ fps: this.TARGET_FRAME_RATE,
+ duration: 1000*1000 });
+ this._frame = 0;
+
+ let me = this;
+ this._timeline.connect('new-frame',
+ function(timeline, frame) {
+ me._onNewFrame(frame);
+ });
+ },
+
+ _onNewFrame : function(frame) {
+ // Unfortunately the interface to to send a new frame to tweener
+ // is a simple "next frame" and there is no provision for signaling
+ // that frames have been skipped or just telling it the new time.
+ // But what it actually does internally is just:
+ //
+ // _currentTime += 1000/_ticker.FRAME_RATE;
+ //
+ // So by dynamically adjusting the value of FRAME_RATE we can trick
+ // it into dealing with dropped frames.
+
+ let delta = frame - this._frame;
+ if (delta == 0)
+ this.FRAME_RATE = this.TARGET_FRAME_RATE;
+ else
+ this.FRAME_RATE = this.TARGET_FRAME_RATE / delta;
+ this._frame = frame;
+ this.emit('prepare-frame');
+ },
+
+ start : function() {
+ this._timeline.start();
+ },
+
+ stop : function() {
+ this._timeline.stop();
+ this._frame = 0;
+ }
+};
+
+Signals.addSignalMethods(ClutterFrameTicker.prototype);
+
function start() {
let global = Shell.global_get();
+ Tweener.setFrameTicker(new ClutterFrameTicker());
+
// The background color really only matters if there is no desktop
// window (say, nautilus) running. We set it mostly so things look good
// when we are running inside Xephyr.